#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
THN DATA ABSTRACTION LAYER version 1

Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

This library is dual-licensed.
  + For commercial software, please obtain a commercial license from THN Solutions LLC.

  + For free software, this library is licensed under GPL version 3. A summary of GPLv3 is
    listed below. You should also find a copy of GPLv3 as file License_GPLv3.txt included
	with the source-code files.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 3 as published by
    the Free Software Foundation  of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#endregion

#region Using directives
using System;
using System.Collections;
using Thn.Application.Sort;
#endregion

namespace Thn.Data.Query
{
    /// <summary>
    /// Represents the Select expression
    /// </summary>
    public class SelectQuery: ConditionalQuery
    {
        internal bool HasDistinct = false;

        #region Properties

        #region Select Clause
        private SelectInfoCollection mSelectClause;
        /// <summary>
        /// Gets the Selects information
        /// </summary>
        public SelectInfoCollection SelectClause
        {
            get { return mSelectClause; }
        }
        #endregion

        #region Field Clause
        private ArrayList mFieldClause;
        /// <summary>
        /// Gets Fields information. 
        /// <para>If null, the fields will be inferred from object type.</para>
        /// <para>If set, the fields are selective. The DAL will execute query against these fields only</para>
        /// </summary>
        public ArrayList FieldClause
        { get { return mFieldClause; } }
        #endregion
		
        #region From Clause
        private ArrayList mFromClause;
        /// <summary>
        /// Gets From information.
        /// <para>If an item is Type, database need to convert to the corresponding table name</para>
        /// <para>If an item if String, database need to use the exact table (or stored procedure) name</para>
        /// </summary>
        public ArrayList FromClause
        {
            get { return mFromClause; }
        }
        #endregion

        #region Order By Clause
        private OrderByInfoCollection mOrderByClause;
        /// <summary>
        /// Gets the OrderBy information
        /// </summary>
        public OrderByInfoCollection OrderByClause
        {
            get { return mOrderByClause; }
        }
        #endregion

        #region Items To Take
        private int mItemsToTake = -1;
        /// <summary>
        /// Gets/Sets maximum number of items to take.
        /// <para>A negative value means this property is disabled.</para>
        /// </summary>
        public int ItemsToTake
        {
            get { return mItemsToTake; }
            set { mItemsToTake = value; }
        }
        #endregion

        #region Items To Skip
        private int mItemsToSkip = -1;
        /// <summary>
        /// Gets/Sets number of of items from the matching results to skip.
        /// <para>A negative value means this property is disabled.</para>
        /// </summary>
        public int ItemsToSkip
        {
            get { return mItemsToSkip; }
            set { mItemsToSkip = value; }
        }
        #endregion

        #endregion

        #region Select
        #region Append Select
        void AppendSelect(SelectInfo info)
        {
            if (mSelectClause == null) mSelectClause = new SelectInfoCollection();
            mSelectClause.Add(info);
        }
        #endregion

        #region Distinct
        /// <summary>
        /// Select fields with Distinct aggregate function
        /// </summary>
        /// <param name="fieldNames">Name of fields to be included in query</param>
        public SelectQuery Distinct(params string[] fieldNames)
        {
            HasDistinct = true;
            for (int i = 0; i < fieldNames.Length; i++)
            {
                AppendSelect(new SelectInfo(fieldNames[i], SelectType.Distinct));
            }
            return this;
        }
        #endregion

        #region Fields
        /// <summary>
        /// Specifies the exact field names
        /// </summary>
        /// <param name="names">Names of Object's fields</param>
        public SelectQuery Fields(params string[] names)
        {
            if (mFieldClause == null) mFieldClause = new ArrayList(names.Length);
            for (int i = 0; i < names.Length; i++)
            {
                mFieldClause.Add(names[i]);
            }

            return this;
        }
        #endregion

        #region Average
        /// <summary>
        /// Select fields with Average aggregate function
        /// </summary>
        /// <param name="fieldNames">Name of fields to be included in query</param>
        public SelectQuery Average(params string[] fieldNames)
        {
            for (int i = 0; i < fieldNames.Length; i++)
            {
                AppendSelect(new SelectInfo(fieldNames[i], SelectType.Avg));
            }
            return this;
        }
        #endregion

        #region Max
        /// <summary>
        /// Select fields with Max aggregate function
        /// </summary>
        /// <param name="fieldNames">Name of fields to be included in query</param>
        public SelectQuery Max(params string[] fieldNames)
        {
            for (int i = 0; i < fieldNames.Length; i++)
            {
                AppendSelect(new SelectInfo(fieldNames[i], SelectType.Max));
            }
            return this;
        }
        #endregion

        #region Min
        /// <summary>
        /// Select fields with Min aggregate function
        /// </summary>
        /// <param name="fieldNames">Name of fields to be included in query</param>
        public SelectQuery Min(params string[] fieldNames)
        {
            for (int i = 0; i < fieldNames.Length; i++)
            {
                AppendSelect(new SelectInfo(fieldNames[i], SelectType.Min));
            }
            return this;
        }
        #endregion

        #region Sum
        /// <summary>
        /// Select fields with Sum aggregate function
        /// </summary>
        /// <param name="fieldNames">Name of fields to be included in query</param>
        public SelectQuery Sum(params string[] fieldNames)
        {
            for (int i = 0; i < fieldNames.Length; i++)
            {
                AppendSelect(new SelectInfo(fieldNames[i], SelectType.Sum));
            }
            return this;
        }
        #endregion

        #endregion

        #region From
        #region From names
        ///// <summary>
        ///// Specifies the exact table name(s) or stored procedure(s), function(s) to query from
        ///// </summary>
        //public SelectQuery From(params string[] names)
        //{
        //    if (mFromClause == null) mFromClause = new ArrayList(names.Length);
        //    for (int i = 0; i < names.Length; i++)
        //    {
        //        mFromClause.Add(names[i]);
        //    }

        //    return this;
        //}
        #endregion        
        
        #region From types
        /// <summary>
        /// Specifies the type of object(s) to query from
        /// </summary>
        public SelectQuery From(params Type[] dataTypes)
        {
            if (mFromClause == null) mFromClause = new ArrayList(dataTypes.Length);
            for (int i = 0; i < dataTypes.Length; i++)
            {
                mFromClause.Add(dataTypes[i]);
            }

            return this;
        }
        #endregion        
        #endregion

        #region Order By
        /// <summary>
        /// Sort the results, use ascending sort direction
        /// </summary>
        public SelectQuery OrderBy(params string[] fieldNames)
        {
            if (mOrderByClause == null) mOrderByClause = new OrderByInfoCollection();

            for (int i = 0; i < fieldNames.Length; i++)
            {
                mOrderByClause.Add(new OrderByInfo(fieldNames[i]));
            }

            return this;
        }

        /// <summary>
        /// Sort the result by a specific field and sort direction
        /// </summary>
        public SelectQuery OrderBy(string fieldName, SortDirection direction)
        {
            if (mOrderByClause == null) mOrderByClause = new OrderByInfoCollection();

            mOrderByClause.Add(new OrderByInfo(fieldName, direction));

            return this;
        }
        #endregion

        #region Take
        /// <summary>
        /// Limit the number of items to take from query.
        /// </summary>
        /// <para>Skip and Take are used together for paging purposes.</para>
        /// <param name="numberOfItems">The maximum number of results to return.</param>
        public SelectQuery Take(int numberOfItems)
        {
            mItemsToTake = numberOfItems;
            return this;
        }
        #endregion

        #region Skip
        /// <summary>
        /// How many items to skip from the matching results.
        /// <para>Skip and Take are used together for paging purposes.</para>
        /// </summary>
        public SelectQuery Skip(int items)
        {
            mItemsToSkip = items;
            return this;
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public SelectQuery()
        { }
        #endregion

        #region To String
        /// <summary>
        /// Convert this query to text form.
        /// <para>NOTE: Do not use this method to obtain the SQL query. It will not be correct.</para>
        /// </summary>
        public override string ToString()
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            //SELECT
            if (mSelectClause != null) sb.Append(mSelectClause.ToString());

            //FIELD
            if (mFieldClause != null)
            {
                for (int i = 0; i < mFieldClause.Count; i++)
                {
                    if (i > 0) sb.Append(", ");
                    sb.Append(mFieldClause[i].ToString());                    
                }
            }

            //FROM
            if (mFromClause != null)
            {
                sb.Append(" FROM ");
                for (int i = 0; i < mFromClause.Count; i++)
                {
                    if (i > 0) sb.Append(", ");
                    sb.Append(mFromClause[i].ToString());
                }
            }

            //WHERE
            if (WhereClause != null)
            {
                sb.Append(" WHERE ");
                sb.Append(WhereClause.ToString());
            }

            //ORDER BY
            if (mOrderByClause != null) sb.Append(mOrderByClause.ToString());

            //TAKE
            //if (mItemsToTake > -1) sb.Append(" TAKE {0}", mItemsToTake);
            if (mItemsToTake > -1) sb.AppendFormat(" TAKE {0}", mItemsToTake);

            //SKIP
            //if (mItemsToSkip > -1) sb.Append(" SKIP {0}", mItemsToSkip);
            if (mItemsToSkip > -1) sb.AppendFormat(" SKIP {0}", mItemsToSkip);

            return sb.ToString();
        }
        #endregion
    }
}
